/*
* TraceCmd.java --
*
*	This file implements the Tcl "trace" command.
*
* Copyright (c) 1997 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
* 
* Included in SQLite3 port to C# for use in testharness only;  2008 Noah B Hart
*
* RCS @(#) $Id: TraceCmd.java,v 1.6 1999/08/15 19:38:36 mo Exp $
*
*/
using System.Collections;
using System.Text;

namespace tcl.lang
{

  /// <summary> The TraceCmd class implements the Command interface for specifying
  /// a new Tcl command. The method cmdProc implements the built-in Tcl
  /// command "trace" which is used to manupilate variable traces.  See
  /// user documentation for more details.
  /// </summary>

  class TraceCmd : Command
  {

    // Valid sub-commands for the trace command.

    private static readonly string[] validCmds = new string[] { "variable", "vdelete", "vinfo" };

    private const int OPT_VARIABLE = 0;
    private const int OPT_VDELETE = 1;
    private const int OPT_VINFO = 2;

    // An array for quickly generating the Tcl strings corresponding to
    // the TCL.VarFlag.TRACE_READS, TCL.VarFlag.TRACE_WRITES and TCL.VarFlag.TRACE_UNSETS flags.

    private static TclObject[] opStr;

    /*
    *----------------------------------------------------------------------
    *
    * initOptStr --
    *
    *	This static method is called when the TraceCmd class is loaded
    *	into the VM. It initializes the opStr array.
    *
    * Results:
    *	Initial value for opStr.
    *
    * Side effects:
    *	The TclObjects stored in opStr are preserve()'ed.
    *
    *----------------------------------------------------------------------
    */

    private static TclObject[] initOptStr()
    {
      TclObject[] strings = new TclObject[8];
      strings[0] = TclString.newInstance( "error" );
      strings[1] = TclString.newInstance( "r" );
      strings[2] = TclString.newInstance( "w" );
      strings[3] = TclString.newInstance( "rw" );
      strings[4] = TclString.newInstance( "u" );
      strings[5] = TclString.newInstance( "ru" );
      strings[6] = TclString.newInstance( "wu" );
      strings[7] = TclString.newInstance( "rwu" );

      for ( int i = 0; i < 8; i++ )
      {
        strings[i].preserve();
      }

      return strings;
    }
    public TCL.CompletionCode cmdProc( Interp interp, TclObject[] objv )
    {
      int len;

      if ( objv.Length < 2 )
      {
        throw new TclNumArgsException( interp, 1, objv, "option [arg arg ...]" );
      }
      int opt = TclIndex.get( interp, objv[1], validCmds, "option", 0 );

      switch ( opt )
      {

        case OPT_VARIABLE:
        case OPT_VDELETE:
          if ( objv.Length != 5 )
          {
            if ( opt == OPT_VARIABLE )
            {
              throw new TclNumArgsException( interp, 1, objv, "variable name ops command" );
            }
            else
            {
              throw new TclNumArgsException( interp, 1, objv, "vdelete name ops command" );
            }
          }

          TCL.VarFlag flags = 0;

          string ops = objv[3].ToString();
          len = ops.Length;
          {
            for ( int i = 0; i < len; i++ )
            {
              switch ( ops[i] )
              {

                case 'r':
                  flags |= TCL.VarFlag.TRACE_READS;
                  break;

                case 'w':
                  flags |= TCL.VarFlag.TRACE_WRITES;
                  break;

                case 'u':
                  flags |= TCL.VarFlag.TRACE_UNSETS;
                  break;

                default:
                  flags = 0;
                  goto check_ops_brk;

              }
            }
          }

check_ops_brk:
          ;


if ( flags == 0 )
{

  throw new TclException( interp, "bad operations \"" + objv[3] + "\": should be one or more of rwu" );
}

if ( opt == OPT_VARIABLE )
{

  CmdTraceProc trace = new CmdTraceProc( objv[4].ToString(), flags );
  Var.traceVar( interp, objv[2], flags, trace );
}
else
{
  // Search through all of our traces on this variable to
  // see if there's one with the given command.  If so, then
  // delete the first one that matches.


  ArrayList traces = Var.getTraces( interp, objv[2].ToString(), 0 );
  if ( traces != null )
  {
    len = traces.Count;
    for ( int i = 0; i < len; i++ )
    {
      TraceRecord rec = (TraceRecord)traces[i];

      if ( rec.trace is CmdTraceProc )
      {
        CmdTraceProc proc = (CmdTraceProc)rec.trace;

        if ( proc.flags == flags && proc.command.ToString().Equals( objv[4].ToString() ) )
        {
          Var.untraceVar( interp, objv[2], flags, proc );
          break;
        }
      }
    }
  }
}
break;


        case OPT_VINFO:
if ( objv.Length != 3 )
{
  throw new TclNumArgsException( interp, 2, objv, "name" );
}

ArrayList traces2 = Var.getTraces( interp, objv[2].ToString(), 0 );
if ( traces2 != null )
{
  len = traces2.Count;
  TclObject list = TclList.newInstance();
  TclObject cmd = null;
  list.preserve();

  try
  {
    for ( int i = 0; i < len; i++ )
    {
      TraceRecord rec = (TraceRecord)traces2[i];

      if ( rec.trace is CmdTraceProc )
      {
        CmdTraceProc proc = (CmdTraceProc)rec.trace;
        TCL.VarFlag mode = proc.flags;
        mode &= ( TCL.VarFlag.TRACE_READS | TCL.VarFlag.TRACE_WRITES | TCL.VarFlag.TRACE_UNSETS );
        int modeInt = (int)mode;
        modeInt /= ( (int)TCL.VarFlag.TRACE_READS );

        cmd = TclList.newInstance();
        TclList.append( interp, cmd, opStr[modeInt] );
        TclList.append( interp, cmd, TclString.newInstance( proc.command ) );
        TclList.append( interp, list, cmd );
      }
    }
    interp.setResult( list );
  }
  finally
  {
    list.release();
  }
}
break;
      }
      return TCL.CompletionCode.RETURN;
    }
    static TraceCmd()
    {
      opStr = initOptStr();
    }
  } // TraceCmd
  class CmdTraceProc : VarTrace
  {

    // The command holds the Tcl script that will execute. The flags
    // hold the mode flags that define what conditions to fire under.

    internal string command;
    internal TCL.VarFlag flags;

    internal CmdTraceProc( string cmd, TCL.VarFlag newFlags )
    {
      flags = newFlags;
      command = cmd;
    }
    public void traceProc( Interp interp, string part1, string part2, TCL.VarFlag flags )
    {
      if ( ( ( this.flags & flags ) != 0 ) && ( ( flags & TCL.VarFlag.INTERP_DESTROYED ) == 0 ) )
      {
        StringBuilder sbuf = new StringBuilder( command );

        try
        {
          Util.appendElement( interp, sbuf, part1 );
          if ( (System.Object)part2 != null )
          {
            Util.appendElement( interp, sbuf, part2 );
          }
          else
          {
            Util.appendElement( interp, sbuf, "" );
          }

          if ( ( flags & TCL.VarFlag.TRACE_READS ) != 0 )
          {
            Util.appendElement( interp, sbuf, "r" );
          }
          else if ( ( flags & TCL.VarFlag.TRACE_WRITES ) != 0 )
          {
            Util.appendElement( interp, sbuf, "w" );
          }
          else if ( ( flags & TCL.VarFlag.TRACE_UNSETS ) != 0 )
          {
            Util.appendElement( interp, sbuf, "u" );
          }
        }
        catch ( TclException e )
        {
          throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
        }

        // Execute the command.

        interp.eval( sbuf.ToString(), 0 );
      }
    }
  } // CmdTraceProc
}
